package com.example.garbage.controller;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.example.garbage.pojo.Buildings;
import com.example.garbage.pojo.Candidate;
import com.example.garbage.pojo.RespBean;
import com.example.garbage.service.BuildingsService;
import com.example.garbage.service.CandidateService;
import com.google.ortools.Loader;
import com.google.ortools.linearsolver.MPConstraint;
import com.google.ortools.linearsolver.MPObjective;
import com.google.ortools.linearsolver.MPSolver;
import com.google.ortools.linearsolver.MPVariable;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

/**
 * @Author Shunrai
 * @create 2024/4/11 10:04
 * @Version 1.0
 * @Description
 */
@RestController
@RequestMapping("/candidate")
@ApiResponse(responseCode = "404", description = "查询失败")
@ApiResponse(responseCode = "200", description = "查询成功")
@Tag(name = "候选点")
public class CandidateController {
    static {
        Loader.loadNativeLibraries(); // 加载OR-Tools本地库
    }
    @Autowired
    private CandidateService candidateService;
    @Autowired
    private BuildingsService buildingsService;

    @GetMapping("/getAll")
    @Operation(summary = "获取所有")
    public RespBean<Candidate> getAll(){
        List<Candidate> list = candidateService.list();
        return RespBean.success("查询成功",list);
    }

    @PostMapping("/decision")
    @Operation(summary = "选址")
    public RespBean<SiteResponse> siteDecision(@RequestBody()@Parameter(description = "请求体参数") List<String> param){

        String[] array = param.toArray(new String[0]);
        List<Candidate> list = candidateService.list();
//        System.out.println("------------");
//        System.out.println(list);
        int[] collect = list.stream().mapToInt(v -> v.getId()).toArray();
        // 将 int 数组转换为 Integer 数组
        Integer[] integerArray = IntStream.of(collect)
                .boxed()  // 转换为包装类 Integer
                .toArray(Integer[]::new);
        //计算od距离
        candidateService.computeOD(array,integerArray);
        QueryWrapper<Buildings> wrapper = new QueryWrapper<Buildings>().in("osm_id",array);
        List<Buildings> buildinglist = buildingsService.list(wrapper);
//        System.out.println("*********");
//        System.out.println(buildinglist);
        // 输入数据
        int numDemandPoints = array.length; // 需求点数量
        int numCandidates = integerArray.length; // 候选服务站数量
        double[] demands = buildinglist.stream().mapToDouble(v->v.getPop()*0.3).toArray(); // 需求点的需求量
        // 需求点和候选服务站之间的距离矩阵
        double[][] distances = new double[array.length][];
        for (int i = 0; i < array.length; i++) {
            distances[i] = candidateService.selectodcost(buildinglist.get(i).getOsmId()).stream()
                    .mapToDouble(Double::doubleValue)
                    .toArray();
        }
        int[] capacities = list.stream().mapToInt(v-> (int) (v.getOgcapacity()*2)).toArray(); // 候选服务站的容量


        // 创建优化器
        MPSolver solver = new MPSolver("ServiceStationProblem", MPSolver.OptimizationProblemType.CBC_MIXED_INTEGER_PROGRAMMING);

        // 创建决策变量
        MPVariable[][] y = new MPVariable[numDemandPoints][numCandidates];
        MPVariable[] x = new MPVariable[numCandidates];
        for (int i = 0; i < numCandidates; i++) {
            x[i] = solver.makeIntVar(0, 1, "x_" + i); // 表示服务站是否被选中
            for (int j = 0; j < numDemandPoints; j++) {
                y[j][i] = solver.makeIntVar(0, 1, "y_" + j + "_" + i); // 表示需求点是否由服务站提供服务
            }
        }

        // 创建约束确保每个需求点只由一个候选点提供服务
        for (int j = 0; j < numDemandPoints; j++) {
            MPConstraint demandConstraint = solver.makeConstraint(1, 1);
            for (int i = 0; i < numCandidates; i++) {
                demandConstraint.setCoefficient(y[j][i], 1);
            }
        }

        // 创建约束条件：服务容量约束
        for (int i = 0; i < numCandidates; i++) {
            MPConstraint capacityConstraint = solver.makeConstraint(0, capacities[i]); // 服务容量约束
            for (int j = 0; j < numDemandPoints; j++) {
                capacityConstraint.setCoefficient(y[j][i], demands[j]); // 服务容量 >= 需求量总和
            }
        }


        // 目标函数：使候选点的数量最小
        MPObjective candidatesObjective = solver.objective();
        for (int i = 0; i < numCandidates; i++) {
            candidatesObjective.setCoefficient(x[i], 1);
        }
        candidatesObjective.setMinimization();

        // 目标函数：使需求点到候选点的距离最小
        MPObjective distanceObjective = solver.objective();
        for (int j = 0; j < numDemandPoints; j++) {
            for (int i = 0; i < numCandidates; i++) {
                distanceObjective.setCoefficient(y[j][i], distances[j][i]);
            }
        }
        distanceObjective.setMinimization();


//        // 创建权衡参数
//        double weightCandidates = 0.00001; // 候选点数量的权重
//        double weightDistances = 1.0; // 距离的权重
//
//        // 创建目标函数：使候选点的数量最小 + 使需求点到候选点的总距离最小
//        MPObjective objective = solver.objective();
//        for (int i = 0; i < numCandidates; i++) {
//            objective.setCoefficient(x[i], weightCandidates); // 候选点数量的权重
//            for (int j = 0; j < numDemandPoints; j++) {
//                objective.setCoefficient(y[j][i], weightDistances * distances[j][i]); // 距离的权重
//            }
//        }
//        objective.setMinimization();


        // 解决问题
        MPSolver.ResultStatus resultStatus = solver.solve();
        List<SiteResponse> siteResponses = new ArrayList<>();
        // 输出结果
        if (resultStatus == MPSolver.ResultStatus.OPTIMAL) {
            System.out.println("最优解找到！");
//            System.out.println("服务站总数：" + candidatesObjective.value());

            // 输出选择的服务站
            System.out.println("选择的服务站:");
            for (int i = 0; i < numCandidates; i++) {
                if (x[i].solutionValue() > 0) {
                    System.out.println("服务站 " + i + " 被选中");
                }
            }

            // 输出需求点与服务站的分配情况
            System.out.println("需求点与服务站的分配情况:");
            for (int j = 0; j < numDemandPoints; j++) {
                for (int i = 0; i < numCandidates; i++) {
                    if (y[j][i].solutionValue() > 0.5) {
                        System.out.println("需求点 " + j + " 被分配给服务站 " + i);
                        String osmId = buildinglist.get(j).getOsmId();
                        Integer id = list.get(i).getId();
                        Double cost = candidateService.getCost(osmId, id);
                        siteResponses.add(new SiteResponse(osmId,id,cost));
                    }
                }
            }
            System.out.println("hhhhhhh");
            System.out.println(siteResponses.stream().map(v->v.getC_id()).distinct().collect(Collectors.toList()).size());
        }  else {
            System.out.println("找不到最优解或结果不满足距离约束。");
        }
        return RespBean.success("成功",siteResponses);
    }
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    static class SiteResponse{
        private String osm_id;
        private Integer c_id;
        private Double cost;
    }
}
